/*
 * CPsFile.cpp
 *
 *  Created on: 2018年7月8日
 *      Author: zhengboyuan
 */

#include "CPsFile.h"
#include "Path.h"



CPsFile::CPsFile():
    m_index(),
    m_stream(),
    m_scanned()
{
    m_buffer.ensure(1024 * 1024);
}

CPsFile::~CPsFile()
{
    close();
}

void CPsFile::setFileIndex(int index)
{
    m_index = index;
}

int CPsFile::getFileIndex()
{
    return m_index;
}

bool CPsFile::open(const char* filepath)
{
    if (!m_stream.open(filepath))
    {
        return false;
    }

    fetchBegin();

    return true;
}

void CPsFile::close()
{
    m_stream.close();

    m_positions.clear();
}

bool CPsFile::isOpen()
{
    return m_stream.isOpen();
}

int64_t CPsFile::getTotalSize()
{
    return comn::Path::getSize(m_filepath);
}

bool CPsFile::getTotalPositionRange(PsFilePosition& beginPos, PsFilePosition& endPos)
{
    if (!hasScanned())
    {
        scanFile();
    }

    if (m_positions.empty())
    {
        return false;
    }

    beginPos = m_positions.front();
    endPos = m_positions.back();

    return true;
}

PsFilePosition CPsFile::getBeginPos()
{
    if (m_positions.empty())
    {
        fetchBegin();
    }

    PsFilePosition position = PsFilePosition();
    if (m_positions.size() > 0)
    {
        position = m_positions.front();
    }
    return position;
}


PsFilePosition CPsFile::getEndPos()
{
    if (!hasScanned())
    {
        scanFile();
    }

    PsFilePosition position = PsFilePosition();
    if (m_positions.size() > 0)
    {
        position = m_positions.back();
    }
    return position;
}

PsFilePosition CPsFile::getBeginKeyPos()
{
    PsFilePosition pos = PsFilePosition();
    if (m_positions.empty())
    {
        return pos;
    }

    PsFilePositionArray::const_iterator it = m_positions.begin();
    for (; it != m_positions.end(); ++ it)
    {
        const PsFilePosition& position = *it;
        if (position.keyframe)
        {
            pos = position;
            break;
        }
    }

    return pos;
}

bool CPsFile::getTotalKeyFrameRange(PsFilePosition& beginPos, PsFilePosition& endPos)
{
    if (!hasScanned())
    {
        scanFile();
    }

    if (m_positions.empty())
    {
        return false;
    }

    bool found = false;

    PsFilePositionArray::const_iterator it = m_positions.begin();
    for (; it != m_positions.end(); ++ it)
    {
        const PsFilePosition& position = *it;
        if (position.keyframe)
        {
            beginPos = position;
            found = true;
            break;
        }
    }

    {
        PsFilePositionArray::const_reverse_iterator it = m_positions.rbegin();
        for (; it != m_positions.rend(); ++ it)
        {
			const PsFilePosition& position = *it;
            if (position.keyframe)
            {
                endPos = position;
                break;
            }
        }
    }

    return found;
}


bool CPsFile::seek(time_t t, PsFileSeekMode seekMode, PsFilePosition& pos)
{
    if (!hasScanned())
    {
        scanFile();
    }

    if (m_positions.empty())
    {
        return false;
    }

    bool found = false;
    size_t idx = find(0, t, seekMode);
	if (idx == -1)
	{
		idx = 0;
	}

    pos = m_positions[idx];

    found = true;

    return found;
}


bool CPsFile::seekRange(time_t t, uint32_t duration, PsFileSeekMode seekMode, PsFilePosition& beginPos, PsFilePosition& endPos)
{
    if (!hasScanned())
    {
        scanFile();
    }

    if (m_positions.empty())
    {
        return false;
    }

    bool found = false;
    size_t beginIndex = find(0, t, seekMode);
    if (beginIndex != -1)
    {
        beginPos = m_positions[beginIndex];
        found = true;
    }

	size_t fromIndex = (beginIndex == -1) ? 0 : beginIndex;
    size_t endIndex = findGreater(fromIndex, t + duration, seekMode);
    if (endIndex != -1)
    {
		/// 文件起始时间大于搜索时间, 但文件结尾时间小于搜索结尾
		if (beginIndex == -1)
		{
			beginIndex = 0;
			beginPos = m_positions[beginIndex];
			found = true;
		}

        endPos = m_positions[endIndex];
    }
    else
    {
        endPos = PsFilePosition();
    }

    return found;
}


bool CPsFile::read(const PsFilePosition& pos, PsMPacket& pkt)
{
    if (pos.size <= 0)
    {
        return false;
    }

	m_buffer.clear();

    bool ret = m_stream.seekg(pos.streamOffset);
    m_stream.read(pos.size, m_buffer);

    pkt.data = m_buffer.data();
    pkt.size = pos.size;

    pkt.type = pos.mediaType;
    pkt.time = pos.time;
    pkt.pts = pos.pts;
    pkt.flags = pos.keyframe;

    return true;
}

bool CPsFile::readRange(const PsFilePosition& beginPos, const PsFilePosition& endPos, PsPacketProc proc, void* userdata)
{
    size_t beginIndex = indexOf(beginPos);
    if (beginIndex == -1)
    {
        return true;
    }

    size_t endIndex = (endPos.size > 0) ? indexOf(endPos) : -1;
    if (endIndex == -1)
    {
        endIndex = m_positions.size();
    }

    bool done = true;

    for (size_t i = beginIndex; i < endIndex; i ++)
    {
        const PsFilePosition& position = m_positions[i];
        PsMPacket pkt;
        if (read(position, pkt))
        {
            if (!(*proc)(userdata, position, pkt))
            {
                done = false;
                break;
            }
        }
        else
        {
            break;
        }
    }
    return done;
}

bool CPsFile::getMediaFormat(MFormat& fmt)
{
    return false;
}

void CPsFile::scanFile()
{
    PsPacket packet = PsPacket();
    while (m_stream.read(packet))
    {
        PsFilePosition position;
        if (convert(m_stream, packet, position))
        {
            m_positions.push_back(position);
        }
    }

    m_scanned = true;

}

bool CPsFile::fetchBegin()
{
    bool found = false;
    int64_t pos = m_stream.tellg();
    if (pos != 0)
    {
        m_stream.seekg(0);
    }

    PsPacket packet = PsPacket();
    while (m_stream.read(packet))
    {
        PsFilePosition position;
        if (convert(m_stream, packet, position))
        {
            m_positions.push_back(position);
            if (position.keyframe)
            {
                found = true;
                break;
            }
        }
    }

	return found;
}

bool CPsFile::convert(PsFileStream& stream, PsPacket& packet, PsFilePosition& position)
{
    bool found = false;
    position.index = m_index;

    if (packet.type == kPsAudioPacket)
    {
        position.mediaType = MTYPE_AUDIO;
        position.keyframe = false;
        position.time = stream.getCurTime();
        PsFileStream::getTimeStamp(packet, position.pts, position.pts);

        PsPacket outPacket;
        PsFileStream::getPesData(packet, outPacket);
        position.size = outPacket.size;

        position.offset = stream.tellg() - packet.size;
        position.streamOffset = stream.tellg() - outPacket.size;

        found = true;
    }
    else if (packet.type == kPsVideoPacket)
    {
        position.mediaType = MTYPE_VIDEO;
        position.keyframe = (stream.getLastPacket().type == kPsPackHeader || stream.getLastPacket().type == kPsPsmHeader);
        position.time = stream.getCurTime();
        PsFileStream::getTimeStamp(packet, position.pts, position.pts);

        PsPacket outPacket;
        PsFileStream::getPesData(packet, outPacket);
        position.size = outPacket.size;

        position.offset = stream.tellg() - packet.size;
        position.streamOffset = stream.tellg() - outPacket.size;

        found = true;
    }
    else if (packet.type == kPsPackHeader)
    {

    }
    return found;
}

bool CPsFile::hasScanned()
{
    return m_scanned;
}

size_t CPsFile::find(size_t offset, time_t t, PsFileSeekMode seekMode)
{
    size_t index = -1;
    for (size_t i = offset; i < m_positions.size(); ++ i)
    {
        const PsFilePosition& position = m_positions[i];
        if (position.time > t)
        {
            break;
        }
        else if (position.time == t)
        {
            if (seekMode == kSeekKeyFrame)
            {
                if (position.keyframe)
                {
                    index = i;
                    break;
                }
            }
            else
            {
                index = i;
                break;
            }
        }
        else
        {
            if (seekMode == kSeekKeyFrame)
            {
                if (position.keyframe)
                {
                    index = i;
                }
            }
            else
            {
                index = i;
            }
        }
    }
    return index;
}

size_t CPsFile::findGreater(size_t offset, time_t t, PsFileSeekMode seekMode)
{
    size_t index = -1;
    for (size_t i = offset; i < m_positions.size(); ++ i)
    {
        const PsFilePosition& position = m_positions[i];
        if (position.time >= t)
        {
            if (seekMode == kSeekKeyFrame)
            {
                if (position.keyframe)
                {
                    index = i;
                    break;
                }
            }
            else
            {
                index = i;
                break;
            }
        }
        else
        {
            //
        }
    }
    return index;
}

PsFilePosition CPsFile::getEndPos(PsFileSeekMode seekMode)
{
    PsFilePosition endPos = PsFilePosition();
    PsFilePositionArray::const_reverse_iterator it = m_positions.rbegin();
    for (; it != m_positions.rend(); ++ it)
    {
        const PsFilePosition& position = *it;

        if (seekMode == kSeekKeyFrame)
        {
            if (position.keyframe)
            {
                endPos = position;
                break;
            }
        }
        else
        {
            endPos = position;
            break;
        }
    }
    return endPos;
}

size_t CPsFile::getEndPosIndex(PsFileSeekMode seekMode)
{
    size_t idx = m_positions.size();
    PsFilePositionArray::const_reverse_iterator it = m_positions.rbegin();
    for (; it != m_positions.rend(); ++ it, idx --)
    {
        const PsFilePosition& position = *it;

        if (seekMode == kSeekKeyFrame)
        {
            if (position.keyframe)
            {
                break;
            }
        }
        else
        {
            break;
        }
    }
    return idx;
}

size_t CPsFile::indexOf(const PsFilePosition& pos)
{
    for (size_t i = 0; i < m_positions.size(); i ++)
    {
        if (pos.offset == m_positions[i].offset)
        {
            return i;
        }
    }
    return -1;
}
